package org.cmd.args;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 解析命令行指令类
 *
 * @param <T>
 */
public class ArgsParser <T extends IArgsOptions> {

	private T options;
	
	public  ArgsParser(Class<T> clazz){
		try {
			this.options = clazz.newInstance();
		} catch (InstantiationException e) {
		} catch (IllegalAccessException e) {
		}
	}
	
	/**
	 * 解析命令行指令
	 * 
	 * @param args
	 * @return
	 */
	public T parse(String[] args){
		List<Command> cmds = options.getUsefulComands();
		Map<String, Command> cmdMap = getCommandMap(cmds);
		
		Command helpCmd = options.getHelpCommand();
		if(helpCmd != null){
			cmdMap.remove(helpCmd.getName());
		}
		if(cmdMap.isEmpty()){
			return options;
		}
		
		boolean showHelp = showHelp(args, helpCmd, cmdMap);
		
		if(showHelp){
			if(helpCmd != null){
				System.out.println(getFixedString(helpCmd.getName(), 15) + helpCmd.getDescribe());
			}
			for (Command command : cmds) {
				String name = command.getName();
				if(cmdMap.containsKey(name)){
					System.out.println(getFixedString(name, 15) + command.getDescribe());
				}
			}
			options.setWrong();
		}
		
		return options;
	}

	private boolean showHelp(String[] args, Command helpCmd, Map<String, Command> cmdMap) {
		String helpCmdName = null;
		if(helpCmd != null){
			helpCmdName = helpCmd.getName();
		}
		Set<String> names = cmdMap.keySet();
		
		List<String> usedCmdList = new ArrayList<String>();
		List<String> requiredCmdList = getRequiredCmds(cmdMap);
		
		boolean showHelp = false;
		if(args != null){
			boolean readCmd = true;
			String comandName = null;
			String para = null;
			for (int i = 0; i < args.length; i++) {
				if(readCmd){
					comandName = args[i].toLowerCase();
					if(helpCmdName != null && helpCmdName.equals(comandName)){
						showHelp = true;
						break;
						
					}else if(names.contains(comandName)){
						Command cmd = cmdMap.get(comandName);
						if(cmd.hasPara()){
							if(i + 1 >= args.length){
								System.out.println(comandName + " 指令需要明确参数!");
								System.out.println(getFixedString(comandName, 15) + cmd.getDescribe());
								options.setWrong();
								return false;
							}
							readCmd = false;
						}else {
							options.setComandPara(comandName, Boolean.TRUE.toString());
							usedCmdList.add(comandName);
							readCmd = true;
						}
					}else {
						System.out.println("该指令未定义：" + comandName);
					}
				}else {
					para = args[i].toLowerCase();
					if(names.contains(para)){
						System.out.println(comandName + " 指令需要明确参数!");
						System.out.println(getFixedString(comandName, 15) + cmdMap.get(comandName).getDescribe());
						options.setWrong();
						return false;
					}
					options.setComandPara(comandName, para);
					usedCmdList.add(comandName);
					readCmd = true;
				}
			}
		}
		
		if(showHelp){
			
		}else if(usedCmdList.size() == 0){
			if(requiredCmdList.size() > 0){
				System.out.println("请指定执行指令：");
				showHelp = true;
			}
		}else {
			List<String> lessCmdList = new ArrayList<String>();
			for (String name : requiredCmdList) {
				if(!usedCmdList.contains(name)){
					lessCmdList.add(name);
				}
			}
			if(lessCmdList.size() > 0){
				for (String name : lessCmdList) {
					System.out.println("必须有以下指令：");
					System.out.println(getFixedString(name, 15) + cmdMap.get(name).getDescribe());
				}
				options.setWrong();
				return false;
			}
		}
		return showHelp;
	}
	
	private List<String> getRequiredCmds(Map<String, Command> cmdMap) {
		List<String> requiredCmdList = new ArrayList<String>();
		for (Command cmd : cmdMap.values()) {
			if(cmd.isRequired()){
				requiredCmdList.add(cmd.getName());
			}
		}
		return requiredCmdList;
	}

	private static String getFixedString(String str, int len){
		int hasLen = 0;
		if(str == null){
			str = "";
		}else {
			hasLen = str.length();
		}
		for (int i = hasLen; i < len; i++) {
			str = str + " ";
		}
		return str;
	}

	private Map<String, Command> getCommandMap(List<Command> cmds) {
		Map<String, Command> cmdMap = new HashMap<String, Command>();
		if(cmds != null && cmds.size() > 0){
			for (Command cmd : cmds) {
				cmdMap.put(cmd.getName(), cmd);
			}
		}
		return cmdMap;
	}
	
}
